package com.wsapi.core.dao.impl;

import com.wsapi.commons.vo.Page;
import com.wsapi.commons.vo.PageList;
import com.wsapi.core.dao.BaseDao;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.query.spi.HQLQueryPlan;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Created with IntelliJ IDEA. User: yangyan Date: 13-11-18 Time: 下午9:09 To
 * change this template use File | Settings | File Templates.
 *
 * @param <T> 对象类型
 * @param <PK> 对象主键类型
 */
@Repository
public abstract class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

    protected SessionFactory sessionFactory;
    private Class<T> entityClass;

    protected BaseDaoImpl() {

        Class c = getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClass = (Class<T>) parameterizedType[0];
        }
    }

    @Resource
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public Session getCurrentSession() {
        Session s = sessionFactory.getCurrentSession();
        return s;
    }

    @Override
    public T getById(PK id) {
        return (T) getCurrentSession().get(entityClass, id);
    }

    @Override
    public void save(T model) {
        getCurrentSession().save(model);
    }

    @Override
    public void delete(T model) {
        getCurrentSession().delete(model);
    }

    @Override
    public void delete(PK[] id) {
        getCurrentSession().delete(entityClass.getName(), id);
    }

    @Override
    public void update(T model) {
        getCurrentSession().update(model);
    }

    @Override
    public List<T> findAll() {
        return getCurrentSession().createCriteria(entityClass.getName()).list();
    }

    @Override
    public PageList<T> findByPage(Page page, String hql, List<Object> params) {
        Query q = getCurrentSession().createQuery(hql).setFirstResult((page.getPage() - 1) * page.getPageSize()).setMaxResults(page.getPageSize());
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i, params.get(i));
            }
        }
        page.setTotal(this.getTotalCount(hql, params));
        return new PageList<T>(page, q.list());
    }

    @Override
    public PageList<T> findByPage(Page page) {
        return this.findByPage(page, "from " + this.entityClass.getName(), null);
    }

    @Override
    public void clear() {
        getCurrentSession().clear();
    }

    @Override
    public void flush() {
        getCurrentSession().flush();
    }

    @Override
    public void evict(Object o) {
        getCurrentSession().evict(o);
    }

    @Override
    public boolean isExist(String propertyName, Object value) {
        String hql = "select count(*) from " + entityClass.getName() + " where " + propertyName + "=?";
        Query q = getCurrentSession().createQuery(hql);
        q.setParameter(0, value);
        return (Long) getCurrentSession().createQuery(hql).setParameter(0, value).uniqueResult() > 0;
    }

    @Override
    public long getTotalCount() {
        String hql = "select count(*) from " + entityClass.getName();
        return Long.valueOf(getCurrentSession().createQuery(hql).uniqueResult().toString());
    }

    @Override
    public long getTotalCount(String hql, List<Object> params) {
        Query q = getCurrentSession().createQuery(prepareCountHql(hql));
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i, params.get(i));
            }
        }
        return Long.valueOf(q.uniqueResult().toString());
    }

    protected String prepareCountHql(String hql) {

        String fromHql = hql;

        fromHql = "from" + StringUtils.substringAfter(fromHql, "from");

        fromHql = StringUtils.substringBefore(fromHql, "order by");

        int whereIndex = fromHql.indexOf("where");

        int leftIndex = fromHql.indexOf("left join");

        if (leftIndex >= 0) {

            if (whereIndex >= 0) {

                String temp = StringUtils.substringBefore(fromHql, "left");

                fromHql = temp + " where " + StringUtils.substringAfter(fromHql, "where");

            } else {

                fromHql = StringUtils.substringBefore(fromHql, "left");

            }

        }

        String countHql = "select count(*) " + fromHql;

        return countHql;

    }

    protected String getCountSql(String originalHql, SessionFactory sessionFactory) {

        SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) sessionFactory;

        HQLQueryPlan hqlQueryPlan = sessionFactoryImplementor.getQueryPlanCache().getHQLQueryPlan(originalHql, false, Collections.emptyMap());

        String[] sqls = hqlQueryPlan.getSqlStrings();

        String countSql = "select count(*) from (" + sqls[0] + ") count";

        return countSql;

    }

    public SessionFactoryImplementor getSessionFactoryImplementor() {

        return (SessionFactoryImplementor) sessionFactory;

    }

    public QueryPlanCache getQueryPlanCache() {

        return getSessionFactoryImplementor().getQueryPlanCache();

    }

    public HQLQueryPlan getHqlQueryPlan(String hql) {

        return getQueryPlanCache().getHQLQueryPlan(hql, false, Collections.emptyMap());

    }

    protected String prepareCountSql(String sql) {
        return getCountSql(sql, sessionFactory);

    }

    @Override
    public <T> List<T> findList(Class<T> clazz, String hql, List<Object> params) {
        Query q = getCurrentSession().createQuery(hql);
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i, params.get(i));
            }
        }
        return q.list();
    }

    @Override
    public <T> T getUniqueResult(Class<T> clazz, String hql, List<Object> params) {
        Query q = getCurrentSession().createQuery(hql);
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i, params.get(i));
            }
        }
        return (T) q.uniqueResult();
    }

    @Override
    public <T> List<T> findList(Class<T> clazz, String hql, Object... params) {
        return this.findList(clazz, hql, params == null ? null : Arrays.asList(params));
    }

    @Override
    public <T> T getUniqueResult(Class<T> clazz, String hql, Object... params) {
        return this.getUniqueResult(clazz, hql, params == null ? null : Arrays.asList(params));
    }

    @Override
    public void execute(String hql, List<Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                q.setParameter(i, params.get(i));
            }
        }
        q.executeUpdate();
    }

    @Override
    public void execute(String hql, Object... params) {
        this.execute(hql, params == null ? null : Arrays.asList(params));
    }



}
